package com.njcb.ams.bootconfig;

import com.njcb.ams.support.config.AmsConfigUtil;
import com.njcb.ams.support.security.bo.SessionModel;
import com.njcb.ams.support.security.config.AmsSecurityConfiguration;
import com.njcb.ams.support.security.config.DefaultAmsSecurityConfiguration;
import com.njcb.ams.support.security.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.session.*;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.ArrayList;
import java.util.List;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class AmsSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private CustomUserDetailsService customUserDetailsService;
    @Autowired
    private LoginSuccessAuthenticationHandler successAuthenticationHandler;
    @Autowired
    private LoginFailureAuthenticationHandler failureAuthenticationHandler;
    @Autowired
    private LogoutAuthenticationHandler logoutAuthenticationHandler;
    @Autowired
    private DefaultAmsSecurityConfiguration defaultAmsSecurityConfiguration;
//	@Autowired
//	private SessionManagerConfiguration.CustomSessionManager customSessionManager;

    @Override
    protected void configure(HttpSecurity http) throws Exception { // 配置策略
        AmsSecurityConfiguration securityConfig = defaultAmsSecurityConfiguration;
        if (null != AmsConfigUtil.getBean(AmsSecurityConfiguration.class)) {
            securityConfig = AmsConfigUtil.getBean(AmsSecurityConfiguration.class);
        }

        securityConfig.configureHttpSecurity(http);

        http.headers().frameOptions().disable();// 允许frame访问策略

        http.headers().cacheControl().disable();//启用浏览器缓存

        //跨域支持
        http.cors().configurationSource(corsConfigurationSource())
                .and().csrf().disable();

        List<String> antPatterns = new ArrayList<String>();
        antPatterns.add("/static/**");
        antPatterns.add("/favicon.ico");
        antPatterns.add("/actuator/**");
        antPatterns.add("/services/**");
        antPatterns.add("/open/**");
        //以下为swagger
        antPatterns.add("/doc.html");
        antPatterns.add("/swagger-ui.html");
        antPatterns.add("/v2/api-docs");
        antPatterns.add("/configuration/**");
        antPatterns.add("/swagger-resources");
        antPatterns.add("/swagger-resources/**");
        antPatterns.add("/swagger-ui.html");
        antPatterns.add("/webjars/**");

        antPatterns.add("/error");
        antPatterns.add("/session/invalid");


        securityConfig.permitMatchers(antPatterns);
        String[] matchers = antPatterns.toArray(new String[antPatterns.size()]);

        //URL权限匹配设置
        http.authorizeRequests()
                .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                .antMatchers(matchers)
                .permitAll().anyRequest().authenticated();// 其他URL需要认证

        //登录相关配置
        http.formLogin().loginPage("/").loginProcessingUrl("/login")
                .successHandler(successAuthenticationHandler)
                .failureHandler(failureAuthenticationHandler).permitAll()
                .and().logout().logoutUrl("/logout")
                .addLogoutHandler(logoutAuthenticationHandler).permitAll();

        //添加一个与UsernamePasswordAuthenticationFilter同级别的过滤器，仅在/login的url执行
        http.addFilterAt(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        http.exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint("/"))
                .accessDeniedHandler(new CustomAccessDeineHandler());

        //最多1人同时登录
        http.sessionManagement().maximumSessions(1).expiredUrl("/session/invalid").sessionRegistry(sessionRegistry());

        if (SessionModel.TOKEN.equals(securityConfig.sessionModel())) {
            //添加一个比UsernamePasswordAuthenticationFilter先执行的过滤器
            http.addFilterBefore(new TokenAuthFilter(authenticationManager()), FilterSecurityInterceptor.class);
            //使用token认证时应禁用session
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }
    }

    /**
     * 跨越访问配置
     *
     * @return
     */
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true);
        //同源配置，*表示任何请求都视为同源，若需指定ip和端口可以改为如“localhost：8080”，多个以“，”分隔；
        corsConfiguration.addAllowedOrigin("*");
        //header，允许哪些header，本案中使用的是token，此处可将*替换为token；
        corsConfiguration.addAllowedHeader("*");
        //允许的请求方法，PSOT、GET等
        corsConfiguration.addAllowedMethod("*");
        //配置允许跨域访问的url
        ((UrlBasedCorsConfigurationSource) source).registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }

    @Bean
    public CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
        CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
        filter.setAuthenticationSuccessHandler(successAuthenticationHandler);
        filter.setAuthenticationFailureHandler(failureAuthenticationHandler);
        filter.setFilterProcessesUrl("/login");

        //重用WebSecurityConfigurerAdapter配置的AuthenticationManager，不然要自己组装AuthenticationManager
        filter.setAuthenticationManager(authenticationManagerBean());
        filter.setSessionAuthenticationStrategy(compositeSessionAuthenticationStrategy());
        return filter;
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(daoAuhthenticationProvider());
        auth.eraseCredentials(false);
    }

    @Bean
    public AuthenticationProvider daoAuhthenticationProvider() {
        CustomDaoAuthenticationProvider daoAuthenticationProvider = new CustomDaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(customUserDetailsService);
		//不隐藏用户不存在错误
        daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        return daoAuthenticationProvider;
    }

    @Bean
    public PasswordEncoder passwordEncoder() { // 密码加密
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Bean
    public SessionRegistry sessionRegistry() {
//		SessionRegistry sessionRegistry = new SessionManagerConfiguration.CustomSessionRegistry(customSessionManager);
        return new SessionRegistryImpl();
    }


    private CompositeSessionAuthenticationStrategy compositeSessionAuthenticationStrategy() {
        List<SessionAuthenticationStrategy> list = new ArrayList<>();
        list.add(new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry()));
        list.add(new SessionFixationProtectionStrategy());
        list.add(new RegisterSessionAuthenticationStrategy(sessionRegistry()));
        CompositeSessionAuthenticationStrategy sessionAuthenticationStrategy = new CompositeSessionAuthenticationStrategy(list);
        return sessionAuthenticationStrategy;
    }
}